package org.javaongems.client.vcl;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONBoolean;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;

public class JsonComponentReader implements ComponentReader {
	final static public String DEFERRED_REF_CARGO = "deferred-ref-cargo";
	
	protected Map componentSpace;		// root container read scope
	protected Container container;
	protected Map cargo;

	protected JSONArray data;			// individual container read scope
	protected int indexPos = 0;
	
	protected Component parent;
	protected Component component;
	
	public JsonComponentReader() {
	}
	
	private JsonComponentReader(JSONArray src, Container targ, Map cmpSpace, Map crgo) {
		container = targ;
		componentSpace = cmpSpace;
		cargo = crgo;
	}
	
	public void readRootContainer(String enc, Map cmpSpace, Container targ) {
		container = targ;
		componentSpace = cmpSpace;
		cargo = new HashMap();
		cargo.put(DEFERRED_REF_CARGO, new HashMap());
		setComponent(targ);
		setParent(null);
		JSONArray parsedData = (JSONArray) JSONParser.parse(enc);
		setComponentsJsonArray(parsedData);
		loadContainer();
	}
	
	protected void setComponentsJsonArray(JSONArray cmpAsJsonArr) {
		data = cmpAsJsonArr;
	}
	
	protected void loadContainer() {
		container.modifyStateMask(Component.LOADING, true);
		try {
			container.readState(this);
		} finally {
			container.modifyStateMask(Component.LOADING, false);	
		}
		container.loaded(this);
		container.invalidate();
//		container.repaint();
	}
	
	public double readNumber() {
		JSONNumber ret = (JSONNumber) data.get(indexPos++);
		return ret.getValue();
	}
	
	public boolean readBoolean() {
		JSONBoolean ret = (JSONBoolean) data.get(indexPos++);
		return ret.booleanValue();
	}
	
	public String readString() {
		JSONString ret = (JSONString) data.get(indexPos++);
		return ret.stringValue();
	}
	
	public Object readNull() {
		data.get(indexPos++);
		return null;
	}
	
	public PropertiesReader readProperties() {
		JSONObject props = (JSONObject) data.get(indexPos++);
		PropertiesReader ret = new JsonPropertiesReader(this, "root", props);
		return ret;
	}
	
	public void readComponent() {
		JSONArray jsonCmp = (JSONArray) data.get(indexPos++);
		JSONString componentName = (JSONString) jsonCmp.get(0);
		Component child = (Component) componentSpace.get(componentName.stringValue());
		JsonComponentReader rdr = new JsonComponentReader(jsonCmp, container, componentSpace, cargo);
		rdr.setComponentsJsonArray(jsonCmp);
		rdr.setParent(component);
		rdr.setComponent(child);
		child.modifyStateMask(Control.LOADING, true);
		try {
			child.readState(rdr);	
		} finally {
			child.modifyStateMask(Control.LOADING, false);
		}
		
	}
	
	public Container getContainer() {
		return container;
	}
	
	public Component getParent() {
		if (parent == null)
			return container;
		return parent;
	}
	
	public Component getComponent() {
		return component;
	}
	
	public void setParent(Component prnt) {
		parent = prnt;
	}
	
	public void setComponent(Component cmp) {
		component = cmp;
	}
	
	public Map getComponentSpace() {
		return componentSpace;
	}
	
	public Map getCargo() {
		return cargo;
	}
	
	public void addDeferredReference(Component src, String componentName, Component component) {
		HashMap map = (HashMap) cargo.get(DEFERRED_REF_CARGO);
		HashMap srcDfs = (HashMap) map.get(src);
		if (srcDfs == null) {
			srcDfs = new HashMap();
			map.put(src, srcDfs);
		}
		srcDfs.put(componentName, component);
	}
	
	public Component getDeferredReference(Component src, String componentName) {
		HashMap map = (HashMap) cargo.get(DEFERRED_REF_CARGO);
		HashMap srcDfs = (HashMap) map.get(src);
		return (Component) srcDfs.get(componentName);
	}
	
	static public class JsonPropertiesReader implements PropertiesReader {
		protected ComponentReader componentReader;
		protected JSONObject properties;
		protected String namedProperties;

		public JsonPropertiesReader(ComponentReader rdr, String name, JSONObject props) {
			componentReader = rdr;
			properties = props;
			namedProperties = name;
			if (properties == null)
				properties = new JSONObject();
		}
		
		public String getNamedProperties() {
			return namedProperties;
		}
		
		public ComponentReader getComponentReader() {
			return componentReader;
		}

		public double readNumber(String name, double defVal) {
			JSONNumber propVal = (JSONNumber) properties.get(name);
			if (propVal == null)
				return defVal;
			return propVal.getValue();
		}

		public boolean readBoolean(String name, boolean defVal) {
			JSONBoolean propVal = (JSONBoolean) properties.get(name);
			if (propVal == null)
				return defVal;
			return propVal.booleanValue();
		}

		public String readString(String name, String defVal) {
			JSONString propVal = (JSONString) properties.get(name);
			if (propVal == null)
				return defVal;
			return propVal.stringValue();
		}

		public Object readNull(String name) {
			JSONValue propVal = (JSONValue) properties.get(name);
			if (propVal.isNull() == null)
				throw new IllegalStateException("property[" + name + "] is not null");
			return null;
		}
		
		public PropertiesReader readNamedProperties(String name) {
			JSONObject propVal = (JSONObject) properties.get(name);
			PropertiesReader pw = new JsonPropertiesReader(componentReader, name, propVal);
			return pw;
		}
	}
}
